package com.jxb.netty.network.nio.write;

import com.jxb.netty.network.nio.Const;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.charset.StandardCharsets;
import java.util.Iterator;
import java.util.Set;

public class NioServerHandle implements Runnable{

    private volatile boolean started;
    private ServerSocketChannel serverSocketChannel;
    private Selector selector;


    public NioServerHandle(int port) {
        try {
            //创建选择器实列
            selector = Selector.open();
            //创建ServerSocketChannel实列
            serverSocketChannel = ServerSocketChannel.open();
            //设置通道为非阻塞模式
            serverSocketChannel.configureBlocking(false);
            //指定端口
            serverSocketChannel.socket().bind(new InetSocketAddress(port));
            //注册事件，关注客户端连接
            serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
            started = true;
            System.out.println("服务器已启动，端口号："+port);
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    @Override
    public void run() {
        while (started) {
            try {
                //获取当前有哪些事件
                selector.select(1000);
                //获取事件的集合(Selector监听所有事件)
                Set<SelectionKey> selectionKeys = selector.selectedKeys();
                Iterator<SelectionKey> iterator = selectionKeys.iterator();
                while (iterator.hasNext()) {
                    SelectionKey key = iterator.next();
                    iterator.remove();
                    handleput(key);
                }
            }catch (Exception e) {

            }
        }
    }

    private void handleput(SelectionKey key) throws IOException {
        if (key.isValid()) {
            if (key.isAcceptable()) {
                ServerSocketChannel ssc = (ServerSocketChannel) key.channel();
                //接收连接
                SocketChannel sc = ssc.accept();
                System.out.println("==========建立连接=========");
                sc.configureBlocking(false);
                //向Selector中注册一个读事件
                sc.register(selector,SelectionKey.OP_READ);
            }
            if (key.isReadable()) {
                SocketChannel sc = (SocketChannel) key.channel();
                ByteBuffer buffer = ByteBuffer.allocate(1024);
                int read = sc.read(buffer);
                if (read > 0) {
                    //将缓冲区的limit设置为position，position=0，用于后续对缓冲区的读取操作
                    buffer.flip();
                    //根据缓冲区的可读字节创建字节数组
                    byte[] bytes = new byte[buffer.remaining()];
                    //将缓冲区中的可读数据复制到新的数组中
                    buffer.get(bytes);
                    String msg = new String(bytes, StandardCharsets.UTF_8);
                    System.out.println("服务器收到消息："+msg);
                    //处理数据-业务逻辑

                    //做出应答
                    String result = Const.response(msg);
                    //发送应答消息
                    doWrite(sc,result);

                }else if(read<0){
                    /*取消特定的注册关系*/
                    key.cancel();
                    /*关闭通道*/
                    sc.close();
                }
            }
            if (key.isWritable()) {
                SocketChannel sc = (SocketChannel) key.channel();
                ByteBuffer buffer = (ByteBuffer)key.attachment();
                System.out.println("进入写事件111111111111111111");
                if(buffer.hasRemaining()){
                    int count = sc.write(buffer);
                    System.out.println("write :"+count
                            +"byte, remaining:"+buffer.hasRemaining());
                }
                else{
                    /*取消对写的注册*/
                    key.interestOps(SelectionKey.OP_READ);
                }
            }
        }
    }

    private void doWrite(SocketChannel sc, String response) throws IOException {
        //将消息编码为字节数组
        byte[] bytes = response.getBytes();
        //根据数组容量创建ByteBuffer
        ByteBuffer writeBuffer = ByteBuffer.allocate(bytes.length);
        //将字节数组复制到缓冲区
        writeBuffer.put(bytes);
        //flip操作
        writeBuffer.flip();
        sc.register(selector,SelectionKey.OP_WRITE|SelectionKey.OP_READ,
                writeBuffer);
    }

    public void stop(){
        started = false;
    }
}
